/**
 * weapp-js v1.7.1(2021-4-16)
 * https://codeup.aliyun.com/motigo/motigo/weapp_base
 * Copyright@ 2021 Motigo, Inc.
 * 
 */
import preload from './preload.js';
import ConsoleLog from "./log.js";
// InnerAudioContext扩展，规避一些无法播放，无回调等问题
class InnerAudioContextEx {
    constructor() {
        this._audio = null;
        this._last_url = null;
        this.urls = '';
        this.opts = {};
        this.play_opts = {};
    }

    play(urls, opts) {
        // 停止上一个        
        this.stop();

        this.urls = urls;

        this.play_opts = {};

        opts = opts || {}
        for (let k in opts) {
            //this.opts[k] = opts[k];
            this.play_opts[k] = opts[k];
        }

        this.opts = opts;

        if (urls instanceof Array) {
            console.log(`[mox.audio]==============req play ${urls.length} audio, urls: `, urls)
            this._multiPlayCallback(urls, 0, opts);
        } else if (typeof (urls) == 'string') {
            console.log(`[mox.audio]==============req play url: ${urls}`)
            this._play(urls, opts);
        } else {
            console.error('[mox.audio]invalid play urls param:', urls);
            opts.fail && opts.fail({
                url: urls,
                err: 'invalid urls param'
            });

            opts.complete && opts.complete({
                url: urls,
                err: 'invalid urls param'
            });
        }
    }

    _multiPlayCallback(urls, index, opts) {
        opts.result = opts.result || {
            successed: [],
            failed: []
        }; // 通过opts.result保存过程结果
        if (index < urls.length) {
            this._play(urls[index], {
                success: res => {
                    opts.result.successed.push(res.url);
                    console.log(`[mox.audio]play successed: ${res.url}`);
                },
                fail: res => {
                    console.warn(`[mox.audio]play fail: ${res.url}`);
                    opts.result.failed.push(res.url);
                },
                complete: () => {
                    this._multiPlayCallback(urls, index + 1, opts);
                },
                begin: res => {
                    res;
                },
                progress: res => {
                    //console.log('play progress:' ,res);
                    res;
                }
            })
        } else { // play end
            if (opts.result.successed.length == urls.length) {
                opts.success && opts.success(opts.result);
            } else {
                opts.result.err = `${opts.result.failed.length} failed`;
                opts.fail && opts.fail(opts.result);
            }

            opts.complete && opts.complete(opts.result);
            this.opts = {}
        }
    }

    stop() {
        if (this._audio) {
            this._audio.offError();
            this._audio.offPlay();
            this._audio.offEnded();
            this._audio.offWaiting();
            this._audio.stop();
            this._audio.destroy();
            this._audio = null;

            this._emitExclusiveEvent(this.opts, 'complete', {
                url: this.urls
            });
        }
    }

    pause() {
        if (this._audio) {
            // this._audio.required_pause = true;  // 标记为主动暂停
            this._audio.pause();
            return true;
        } else {
            return false;
        }
    }

    resume() {
        console.log('[mox.audio]resume');
        if (this._audio && this._audio.paused && (!this._audio.ended)) {
            if (!this._audio.playing) {
                console.log('[mox.audio]replay audio');
                this._audio.stop();
                if(this._last_url && typeof this._last_url == "string" && this._last_url.length > 0){
                    this._audio.src = this._last_url;
                }
            } else {
                // this._audio.play(); // 不要和下面重复调用
            }

            this._audio.play();
            return true;
        } else {
            console.log('[mox.audio]ignored');
            return false;
        }
    }

    replay() {
        wx.nextTick(() => {
            this.play(this.urls, this.play_opts);
        });
    }

    // 播放单个
    _play(url, opts) {
        const res_url = preload.getPath(url);
        console.info(`[mox.audio]try play: ${url}, res url: ${res_url}`);
        if (!this._audio) {
            this._audio = wx.createInnerAudioContext();
        }

        this._audio.offError();
        this._audio.offPlay();
        this._audio.offEnded();
        this._audio.offWaiting();
        this._audio.offTimeUpdate();

        this._clearTimer();
        this._audio.ended = false; // ended在onEnded设置，避免resume的时候重新播放

        // 记录是否开始播放，用于规避在iOS上，resume无效的问题
        // 在ios是，如果audio设置了src，但还没有onPlay，这是关闭小程序再打开，调用resume音频不会继续播放！
        // 需要设置url
        this._audio.playing = false;

        this._audio.onError(e => {
            console.error('[mox.audio]on play error:', e);

            // 经验证，如果设置到src的url不符合规则(如非http(s)://开头，又不是本地文件时），会使用之前设置的url播放
            // 为了避免传错误的url却播放之前的音频，这里destroy
            this._audio.destroy();
            this._audio = null;
            this._clearTimer();

            const res = {
                url: url,
                detail:e,
                err: e.errMsg
            };

            this._emitExclusiveEvent(opts, 'fail', res);
            this._emitExclusiveEvent(opts, 'complete', res);

            ConsoleLog.error("play audio error")
            ConsoleLog.error(e)
        });

        this._audio.onWaiting(() => {
            console.info('[mox.audio]on waiting:', url);
            opts.waiting && opts.waiting({
                url: url
            });
        });

        this._audio.onEnded(() => {
            if(this._audio){
                // 防止this._audio被异步设置为null
                this._audio.ended = true;
            }
            this._clearTimer();

            this._emitExclusiveEvent(opts, 'success', {
                url: url
            })

            this._emitExclusiveEvent(opts, 'complete', {
                url: url
            })
        });

        this._audio.onPlay(() => {
            // console.log('on play: ', this._audio.currentTime, this._audio.duration);
            this._audio.playing = true;
            opts.begin && opts.begin({
                url: url
            });
        });

        // 重要！坑：必须设置onCanplay，才会有onTimeUpdate回调
        // this._audio.onCanplay( ()=>{
        //     console.log('on can play: ', this._audio.currentTime, this._audio.duration);            
        // });

        // this._audio.onTimeUpdate( () => {
        //     // console.log('on time update: ', this._audio.currentTime, this._audio.duration);
        //     // if (this._audio.duration > 0 && !this._audio.timeout) {
        //     //     this._audio.timeout = setTimeout(()=>{
        //     //         console.log('on audio timeout', this._audio.currentTime, this._audio.duration, this._audio.paused);

        //     //         const res = {
        //     //             url: url,
        //     //             err: 'timeout'
        //     //         };

        //     //         this._emitExclusiveEvent(opts, 'fail', res);
        //     //         this._emitExclusiveEvent(opts, 'complete', res);
        //     //     }, this._audio.duration*1000+100);
        //     // }
        // });

        this._audio.src = encodeURI(res_url);

        if(opts.loop == true){
            this._audio.loop = true;
        }

        if (this._last_url == this._audio.src) {
            this._audio.seek(0);
            this._audio.play();
        } else {
            this._audio.autoplay = true;
            this._last_url = url; //this._audio.src;
        }
    }

    // 触发opts中的独占的事件，主要是避免多次触发
    _emitExclusiveEvent(opts, evt_name, params) {
        opts = opts || {};
        const cb = opts[evt_name]; // 保存下来，因为需要马上将opts[evt_name]置空, 避免因callback耗时导致重复回调

        opts[evt_name] = null;
        cb && cb(params);
    }

    _clearTimer() {
        if (this._audio && this._audio.timeout) {
            clearTimeout(this._audio.timeout);
            this._audio.timeout = null;
        }
    }
}

export default {
    _audios: [],
    _global_audio: null, // 独占的全局audio
    _inited: false,
    _init() {
        if (this._inited)
            return

        console.log('init audio mgr');
        this._inited = true;
        wx.setInnerAudioOption({
            mixWithOther: true,
            obeyMuteSwitch: false,
            success: () => {
                console.log('set inner audio option succes');
            },
            fail: err => {
                err;
                // console.warn('set inner audio option fail! err:', err);
            }
        });

        wx.onAudioInterruptionEnd(() => {
            console.log('[mox.audio]onAudioInterruptionEnd');
            this.resumeAll();
        });

        wx.onAudioInterruptionBegin(() => {
            console.log('[mox.audio]onAudioInterruptionBegin');
        });

        // wx.onAppShow(()=>{
        //     console.log('onAppShow');
        // });

        // wx.onAppHide(()=>{
        //     console.log('onAppHide');
        // });        
    },
    play(urls, opts) {
        if (!this._global_audio) {
            this._global_audio = this.createAudioContext();
        }

        this._global_audio.play(urls, opts);
    },
    stop() {
        if (this._global_audio) {
            this._global_audio.stop()
        }
    },
    pause() {
        if (this._global_audio) {
            this._global_audio.pause();
            return true;
        } else {
            return false;
        }
    },
    resume() {
        if (this._global_audio) {
            this._global_audio.resume();
        }
    },
    getGlobalAudio() {
        if (!this._global_audio) {
            this._global_audio = this.createAudioContext()
        }

        return this._global_audio;
    },
    createAudioContext() { // 创建
        this._init();

        let obj = new InnerAudioContextEx();
        this._audios.push(obj);
        return obj;
    },
    destroyAudioContext(obj) {
        if (obj) {
            obj.stop();
            const index = this._audios.indexOf(obj);
            if (index > -1) {
                this._audios.splice(index, 1);
            }
        }
    },
    pauseAll() {
        this._audios.forEach(obj => {
            obj.pause();
        });
    },
    resumeAll() {
        this._audios.forEach(obj => {
            obj.resume();
        });
    },
    stopAll() {
        this._audios.forEach(obj => {
            obj.stop();
        });
    },
}